﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Athena.Objects;

namespace Athena.Plugins
{
    public partial class PartViewer : Athena.Tools.IGUITool
    {
        public override void SetupToolStrip(ToolStrip oStrip)
        {
            //ToolStripButton oButton = new ToolStripButton();
            //oButton.Text = Text;
            //oButton.ToolTipText = "Part editor";
            //oButton.Click += new EventHandler(ShowHide_Click);
            //oStrip.Items.Add(oButton);
        }

        public override void SetupDockPanel(WeifenLuo.WinFormsUI.Docking.DockPanel oPanel)
        {
            this.DockPanel = oPanel;
            DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockRight;
            Hide();
        }

        public override void ShowHide_Click(object sender, EventArgs e)
        {
            if (IsHidden)
            {
                Show();
            }
            else
            {
                Hide();
            }
        }
        public PartViewer()
        {
            InitializeComponent();

            SetStyle(ControlStyles.AllPaintingInWmPaint |
                ControlStyles.OptimizedDoubleBuffer |
                ControlStyles.ResizeRedraw |
                ControlStyles.UserPaint, true);

            Text = "Part properties";
            _bHasContextMenuItems = true;
            _bHasMenuItems = false;
            _bRequiresNewToolWindow = false;
            ToolManager.Instance.RegisterTool(Text, this);
            if (!ToolManager.Instance.PropertyEditors.Contains(this))
                ToolManager.Instance.PropertyEditors.Add(this);

            populateComboBox();

            CurrentModel.SelectionChanged += new Model.SelectionChangedDelegate(Model_SelectionChanged);
            CurrentModel.ModelChanged += new Model.ModelChangedDelegate(CurrentModel_ModelChanged);
        }

        void CurrentModel_ModelChanged(object sender, EventArgs e)
        {
            OnChange();
        }

        public override bool Handles(List<Type> oTypes)
        {
            bool bResult = true;
            foreach (Type t in oTypes)
            {
                bResult &= Handles(t);
                if (!bResult) return false;
            }
            return bResult;
        }

        private bool Handles(Type t)
        {
            return (Utility.IsType(typeof(PartGlyph), t));
        }

        public override void SetupContextMenu(ToolStripMenuItem oStrip)
        {
            oStrip.Text = "Part editor...";
            oStrip.ToolTipText = "Provides a convenient way of changing properties of selected part";
            oStrip.Click += new EventHandler(oItem_Click);
        }

        bool _Initializing = false;
        void OnChange()
        {
            if (_Initializing) return;
            _Initializing = true;

            if (CurrentModel.Selected.Count > 0)
            {
                bool allParts = true;

                foreach (IDrawable id in CurrentModel.Selected)
                {
                    if (!(id.Item is Core.Part))
                    {
                        allParts = false;
                        break;
                    }
                    else
                    {
                        reverseCheckBox.Checked = ((Core.Part)id.Item).Direction == Athena.Core.Part.Orientation.reverse;
                        forwardCheckBox.Checked = ((Core.Part)id.Item).Direction == Athena.Core.Part.Orientation.forward;
                    }
                }

                if (!allParts)
                {
                    TargetPart = null;
                    Hide();
                }
                else
                {
                    if (CurrentModel.Selected.Count == 1)
                    {
                        Core.Item item = CurrentModel.Selected[0].Item;
                        if (item == null) return;

                        if (item is Core.Part)
                        {
                            TargetPart = (Core.Part)item;
                        }
                        else
                        {
                            TargetPart = null;
                            Hide();
                        }
                    }
                    if (IsHidden)
                        Show();
                }
            }
            else
            {
                TargetPart = null;
                Hide();
            }
            if (!IsHidden && CurrentModel.Selected.Count > 0)
            {
                propertyGrid1.SelectedObjects = CurrentModel.Selected.ToArray();
            }
            _Initializing = false;
        }

        public override void oItem_Click(object sender, EventArgs e)
        {
            OnChange();
        }

        public override void OnItemDoubleClick(HandledMouseEventArgs e)
        {
            OnChange();
        }

        void Model_SelectionChanged(object sender, Model.SelectionEventArgs e)
        {
            if (!IsHidden)
                OnChange();
        }
        private Core.Part _TargetPart = null;
        public Core.Part TargetPart
        {
            get { return _TargetPart; }
            set
            {
                if (value != null)
                {
                    _TargetPart = value;
                    idText.Text = _TargetPart.ID;
                    sequenceTextBox.Text = _TargetPart.Sequence;
                    forwardCheckBox.Checked = _TargetPart.Direction == Athena.Core.Part.Orientation.forward;
                    reverseCheckBox.Checked = _TargetPart.Direction == Athena.Core.Part.Orientation.reverse;
                    activityText.Text = _TargetPart.PoPS.Rate;
                    UpdateLists();
                }
                else
                {
                    paramsDataGrid.DataSource = null;
                    modifiersDataGrid.DataSource = null;
                    idText.Text = "";
                    activityText.Text = "";
                    sequenceTextBox.Text = "";
                    _TargetPart = null;
                }
            }
        }

        /// <summary>
        /// This class contains a name->Node pair. It is used by PartViewer to manipulate an edge
        /// </summary>
        public class Node : IComparable<Node>
        {
            public int CompareTo(Node other)
            {
                return ID.CompareTo(other.ID);
            }
            public PartViewer viewer;
            public Core.Node _Node;

            public string ID
            {
                get
                {
                    if (_Node == null)
                        return "";
                    else
                        return _Node.ID;
                }
                set
                {
                    if (_Node != null && value != _Node.ID)
                    {
                        HistoryManager.History.SetControllPoint("ID changed : " + value);
                        _Node.ID = value;                        
                        Utility.Refresh();
                        CurrentModel.OnModelChanged(EventArgs.Empty);
                    }
                }
            }
            public double Cooperativity
            {
                get
                {
                    if (viewer != null && viewer.TargetPart != null)
                    {
                        Core.Edge e = viewer.TargetPart.PoPS;
                        if (e != null)
                            return e.GetModifierStoichiometry(_Node);
                        
                    }
                    return double.NaN;
                }
                set
                {
                    if (viewer != null && viewer.TargetPart != null)
                    {
                        HistoryManager.History.SetControllPoint("Stoichiometry changed : " + value);
                        Core.Edge e = viewer.TargetPart.PoPS;
                        if (e != null)
                            e.SetModifierStoichiometry(_Node, value);
                        CurrentModel.OnModelChanged(EventArgs.Empty);
                    }
                }
            }
            public Node(Core.Node n, PartViewer rv)
            {
                _Node = n;
                viewer = rv;
            }
        }
        /// <summary>
        /// This class contains a name->Parameter pair. It is used by ModuleViewer to manipulate an edge
        /// </summary>
        public class Parameter : IComparable<Parameter>
        {
            public int CompareTo(Parameter other)
            {
                return _ID.CompareTo(other._ID);
            }

            public PartViewer viewer;
            private string _ID;
            private Core.Parameter _Parameter;

            public string ID
            {
                get
                {
                    return _ID;
                }
                set
                {
                    if (viewer != null && viewer.TargetPart != null)
                    {
                        Core.Edge e = viewer.TargetPart.PoPS;
                        if (e != null && e.ParentModule != null)
                        {
                            HistoryManager.History.SetControllPoint("Parameter ID changed : " + value);
                            e.ChangeParameterName(_ID, value);
                            CurrentModel.OnModelChanged(EventArgs.Empty);
                        }
                    }
                }
            }
            public double Value
            {
                get
                {
                    if (_Parameter != null)
                        return _Parameter.Value;
                    else
                        return double.NaN;
                }
                set
                {
                    HistoryManager.History.SetControllPoint("Parameter value changed : " + value);
                    _Parameter.Value = value;
                    CurrentModel.OnModelChanged(EventArgs.Empty);
                }
            }

            public Parameter(string n, Core.Parameter parameter, PartViewer rxnviewer)
            {
                _ID = n;
                _Parameter = parameter;
                viewer = rxnviewer;
            }
        }

        public void UpdateLists()
        {
            try
            {
                if (_TargetPart != null)
                {
                    Core.Edge edge = _TargetPart.PoPS;

                    List<Node> _Modifiers = new List<Node>();
                    List<Parameter> _Parameters = new List<Parameter>();

                    Hashtable modifiers = edge.ModifiersHash,
                              parameters = edge.ParametersHash;

                    foreach (Core.Node n in modifiers.Keys)
                    {
                        _Modifiers.Add(new Node(n, this));
                    }
                    foreach (string s in parameters.Keys)
                    {
                        if (parameters[s] is Core.Parameter)
                            _Parameters.Add(new Parameter(s, (Core.Parameter)parameters[s], this));
                    }

                    _Modifiers.Sort();
                    _Parameters.Sort();

                    modifiersDataGrid.DataSource = _Modifiers;
                    paramsDataGrid.DataSource = _Parameters;

                    /*modifiersDataGrid.Refresh();
                    paramsDataGrid.Refresh();*/
                }
            }
            catch (Exception) { }
        }

        private void idText_TextChanged(object sender, EventArgs e)
        {
            if (_Initializing) return;
            if (_TargetPart != null)
            {
                HistoryManager.History.SetControllPoint("Part ID changed: " + _TargetPart.ID);
                _TargetPart.ID = idText.Text;
                CurrentModel.OnModelChanged(EventArgs.Empty);
            }
        }

        private void sequenceText_TextChanged(object sender, EventArgs e)
        {
            if (_Initializing) return;
            if (_TargetPart != null)
            {
                HistoryManager.History.SetControllPoint("Part sequence changed: " + _TargetPart.ID);
                _TargetPart.Sequence = sequenceTextBox.Text;
                CurrentModel.OnModelChanged(EventArgs.Empty);
            }
        }

        private void activityText_TextChanged(object sender, EventArgs e)
        {
            if (_Initializing) return;
            if (_TargetPart != null && _TargetPart.PoPS != null)
            {
                HistoryManager.History.SetControllPoint("Part activity changed: " + _TargetPart.ID);
                _TargetPart.PoPS.Rate = activityText.Text;
                if (_TargetPart.Drawable is Objects.PartGlyph)
                {
                    ((Objects.PartGlyph)_TargetPart.Drawable).GenerateModifiers();
                }
                CurrentModel.OnModelChanged(EventArgs.Empty);
            }
        }

        /// <summary>
        /// Select from one of the available rate laws: not, and, or, xor, nand, etc
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (_Initializing) return;
            if (CurrentModel.Selected.Count < 1) return;

            HistoryManager.History.SetControllPoint("Part(s) activities changed");

            if (CurrentModel.Selected.Count > 0)
            {
                foreach (IDrawable id in CurrentModel.Selected)
                {
                    if (id.Item is Core.Part)
                    {
                        Connector.ArrowHeadType arrow = Connector.ArrowHeadType.Arrow;
                        Core.Part part = (Core.Part)id.Item;

                        bool changed = true;

                        switch (comboBox1.SelectedItem.ToString())
                        {
                            case "AND gate":
                                part.PoPS.Rate = Tools.TranscriptionRate.AND(part.PoPS.Modifiers, part.PoPS.ModifierStoichiometry);
                                arrow = Connector.ArrowHeadType.Circle;
                                break;
                            case "OR gate":
                                part.PoPS.Rate = Tools.TranscriptionRate.OR(part.PoPS.Modifiers, part.PoPS.ModifierStoichiometry);
                                arrow = Connector.ArrowHeadType.Circle;
                                break;
                            case "NAND gate":
                                part.PoPS.Rate = Tools.TranscriptionRate.NAND(part.PoPS.Modifiers, part.PoPS.ModifierStoichiometry);
                                arrow = Connector.ArrowHeadType.Bar;
                                break;
                            case "NOT gate":
                            case "NOR gate":
                                part.PoPS.Rate = Tools.TranscriptionRate.NOR(part.PoPS.Modifiers, part.PoPS.ModifierStoichiometry);
                                arrow = Connector.ArrowHeadType.Bar;
                                break;
                            case "XOR gate":
                                part.PoPS.Rate = Tools.TranscriptionRate.XOR(part.PoPS.Modifiers, part.PoPS.ModifierStoichiometry);
                                arrow = Connector.ArrowHeadType.Bar;
                                break;
                            default:
                                changed = false;
                                break;
                        }
                        if (changed)
                        {
                            part.PoPS.AssignParameterNames();
                            Tools.TranscriptionRate.CopyParameterValues(part.PoPS);
                        }
                        if (_TargetPart == part)
                            TargetPart = part;

                        IGlyph drawable = part.Drawable as IGlyph;
                        if (drawable != null)
                        {
                            foreach (Connector c in drawable.IncomingConnections)
                            {
                                c.ArrowHead = arrow;
                            }
                        }
                    }
                }
                CurrentModel.OnModelChanged(EventArgs.Empty);
                Utility.Refresh();
            }
        }

        void populateComboBox()
        {
            string[] rates = { "AND gate", "OR gate", "NOT gate", "NOR gate", "XOR gate", "NAND gate" };

            foreach (string s in rates)
                comboBox1.Items.Add(s);
        }

        private void dataView_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            Utility.SetMessage("Error in value");
            e.Cancel = true;
        }

        /// <summary>
        /// Prevent some nasty keys ...
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="keyData"></param>
        /// <returns></returns>
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (!Focused) return false;

            switch (keyData)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Back:
                case Keys.Tab:
                case Keys.Enter:
                case Keys.Home:
                case Keys.Home & Keys.Shift:
                    return true;

                default:
                    return base.ProcessCmdKey(ref msg, keyData);
            }
        }

        private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            HistoryManager.History.SetControllPoint("property changed");
            CurrentModel.OnModelChanged(EventArgs.Empty);
            Utility.Refresh();
        }

        private void forwardCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if (_Initializing) return;

            if (sender == forwardCheckBox)
            {
                if (forwardCheckBox.Checked) 
                    reverseCheckBox.Checked = false;
                else
                    reverseCheckBox.Checked = true;
            }
            else
                if (sender == reverseCheckBox)
                {
                    if (reverseCheckBox.Checked)
                        forwardCheckBox.Checked = false;
                    else
                        forwardCheckBox.Checked = true;
                }
                else
                    return;

            if (CurrentModel.Selected.Count < 1) return;
            
            bool atLeastOneChange = false;

            HistoryManager.History.SetControllPoint("Part(s) direction changed");

            List<IDrawable> selected = CurrentModel.Selected;

            foreach (IDrawable id in selected)
            {
                if (id.Item is Core.Part)
                {
                    if (forwardCheckBox.Checked && ((Core.Part)id.Item).Direction != Athena.Core.Part.Orientation.forward)
                    {
                        ((Core.Part)id.Item).Direction = Athena.Core.Part.Orientation.forward;
                        atLeastOneChange = true;
                    }

                    if (reverseCheckBox.Checked && ((Core.Part)id.Item).Direction != Athena.Core.Part.Orientation.reverse)
                    {
                        ((Core.Part)id.Item).Direction = Athena.Core.Part.Orientation.reverse;
                        atLeastOneChange = true;
                    }
                }
                if (TargetPart == id.Item)
                {
                    sequenceTextBox.Text = TargetPart.Sequence;
                }
            }

            if (atLeastOneChange)
            {
                CurrentModel.OnModelChanged(EventArgs.Empty);
            }

            Utility.Refresh();
        }
    }
}
